package com.glc.hms.common.util;

import com.glc.hms.common.constant.FileTypeCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;

/**
 * 文件处理器
 *
 * @author ljt
 * @since 2019-11-26
 */
@Slf4j
public class FileResolver {
    private static final String IMAGE_SPEC_NUM_SPLIT_CHAR = "X";
    private static final String WEB_PLACEHOLDER = "@WEB_PLACEHOLDER@";
    private String tmpPath;//缓存文件保存路径
    private String uploadPath;//上传文件保存路径
    private String fileWebUrl;//文件网络根路径
    private String imgSpec;//图片规格

    public FileResolver(String tmpPath, String uploadPath, String fileWebUrl, String imgSpec) {
        this.tmpPath = tmpPath;
        this.uploadPath = uploadPath;
        this.fileWebUrl = StringUtils.isNotBlank(fileWebUrl) && fileWebUrl.endsWith("/") ? fileWebUrl.substring(0, fileWebUrl.length() - 1) : fileWebUrl;
        this.imgSpec = imgSpec;

        checkDir(tmpPath);
        checkDir(uploadPath);
    }

    public void setTmpPath(String tmpPath) {
        this.tmpPath = tmpPath;
        checkDir(tmpPath);
    }

    public String getTmpPath() {
        return tmpPath;
    }

    public void setUploadPath(String uploadPath) {
        this.uploadPath = uploadPath;
        checkDir(uploadPath);
    }

    public String getUploadPath() {
        return uploadPath;
    }

    public void setFileWebUrl(String fileWebUrl) {
        this.fileWebUrl = fileWebUrl;
    }

    public String getFileWebUrl() {
        return fileWebUrl;
    }

    public void setImgSpec(String imgSpec) {
        this.imgSpec = imgSpec;
    }

    public String getImgSpec() {
        return imgSpec;
    }

    public class FileProtocol {
        private String fileName;//文件名
        private File file;//文件
        private String fileId;
        private String webUrl;//文件网络地址
        private String imgSpec;//文件多规格

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public File getFile() {
            return file;
        }

        public void setFile(File file) {
            this.file = file;
        }

        public String getFileId() {
            return fileId;
        }

        public void setFileId(String fileId) {
            this.fileId = fileId;
        }

        public String getWebUrl() {
            return webUrl;
        }

        public void setWebUrl(String webUrl) {
            this.webUrl = webUrl;
        }

        public String getImgSpec() {
            return imgSpec;
        }

        public void setImgSpec(String imgSpec) {
            this.imgSpec = imgSpec;
        }
    }

    /**
     * 检查路径是否存在
     *
     * @param dir 路径
     * @return
     */
    private boolean checkDir(String dir) {
        File file = new File(dir);
        if (!file.exists()) {
            return file.mkdirs();
        }

        return true;
    }

    /**
     * 获取本地文件
     *
     * @param fileId
     * @return
     */
    public File getLocalFile(String fileId) {
        if (StringUtils.isBlank(uploadPath)) {
            log.error("文件保存路径 uploadPath 不能为空：" + uploadPath);
            throw new RuntimeException("文件保存路径 uploadPath 不能为空：" + uploadPath);
        }

        return new File(uploadPath + fileId);
    }

    /**
     * 文件拷贝
     *
     * @param file   文件
     * @param fileId
     * @return
     */
    private String checkIn(File file, String fileId) {
        OutputStream out = null;
        FileInputStream in = null;
        try {
            out = FileUtils.openOutputStream(getLocalFile(fileId));
            in = FileUtils.openInputStream(file);
            IOUtils.copy(in, out);
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(out);
        } catch (IOException ex) {
            log.error(ex.getMessage(), ex);
        } finally {
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(out);
            file.delete();
        }

        return fileId;
    }

    /**
     * 构建文件ID
     *
     * @param fileName 文件名
     * @return
     */
    private String buildFileId(String fileName) {
        StringBuffer buffer = new StringBuffer();
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(new Date());
        String uuid = UUID.randomUUID().toString();
        /*连锁ID作为第一目录*/
        buffer.append("/").append(cal.get(Calendar.YEAR))
                .append("/").append(cal.get(Calendar.MONTH) + 1)
                .append("/").append(cal.get(Calendar.DAY_OF_MONTH))
                .append("/")
                .append(uuid)
                .append(fileName.substring(fileName.lastIndexOf('.')));

        return buffer.toString();
    }

    /**
     * 获取文件类型枚举
     *
     * @param fileCode 文件类型
     * @return
     */
    private String getFileTypeCode(String fileCode) {
        String result = FileTypeCodeEnum.OTHER.getCode();
        String[] codeImages = {".JPG", ".GIF", ".PNG", ".JPEG"};
        if (checkCode(fileCode, codeImages)) {
            result = FileTypeCodeEnum.IMAGE.getCode();
            return result;
        }
        String[] codeOffices = {".DOC", ".PPT", ".XLS", ".DOCX", ".XLSX", ".PPTX", ".XLSX"};
        if (checkCode(fileCode, codeOffices)) {
            result = FileTypeCodeEnum.OFFICE.getCode();
            return result;
        }
        String[] codeTexts = {".TXT", ".HTML"};
        if (checkCode(fileCode, codeTexts)) {
            result = FileTypeCodeEnum.TEXT.getCode();
            return result;
        }
        String[] codeSounds = {".MP3", ".WAV", ".WMA", ".MIDI"};
        if (checkCode(fileCode, codeSounds)) {
            result = FileTypeCodeEnum.SOUND.getCode();
            return result;
        }
        String[] codeSwfs = {".SWF"};
        if (checkCode(fileCode, codeSwfs)) {
            result = FileTypeCodeEnum.FLASH.getCode();
            return result;
        }
        String[] codeVideos = {".AVI", ".MPEG", ".MPG", ".FLV", ".MKV", ".3GP", ".ASF", ".MOV", ".MP4", ".RMVB"};
        if (checkCode(fileCode, codeVideos)) {
            result = FileTypeCodeEnum.VIDEO.getCode();
            return result;
        }
        return result;
    }

    /**
     * 判断文件类型组
     *
     * @param fileCode 文件类型
     * @param codes    文件类型组
     * @return
     */
    private boolean checkCode(String fileCode, String[] codes) {
        for (String code : codes) {
            if (fileCode.equals(code)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断文件是否存在
     *
     * @param fileId
     * @return
     */
    public boolean fileExists(String fileId) {
        File srcFile = getLocalFile(fileId);
        return srcFile.exists();
    }

    /**
     * 根据传入的规格，输出对应规格的图片文件ID
     *
     * @param srcFileId      源文件
     * @param specs          规格
     * @param isReGenPicture 是否重新生成图片
     * @param force          是否强制生成规格图，可能会变形
     * @return 图片文件ID  1/2016/1/8/XXX-XX-XX-X.jpg
     */
    private String genPicsReturnFileId(String srcFileId, String specs, boolean isReGenPicture, boolean force) {
        if (StringUtils.isBlank(srcFileId)) {
            return "";
        }

        Assert.isTrue(specs.contains(IMAGE_SPEC_NUM_SPLIT_CHAR), "规格分隔符必须为大写的 'X'");
        String fileId = CommonUtil.insertFileNameSuffixToUrl(srcFileId, "_" + specs);
        if (fileExists(fileId)) {
            if (!isReGenPicture) {
                return fileId;
            }
        }

        String[] values = specs.split(IMAGE_SPEC_NUM_SPLIT_CHAR);
        int width = Integer.parseInt(values[0]);
        int height = Integer.parseInt(values[1]);
        try {
            ImageUtil.genPictures(new int[]{width, height}, getLocalFile(srcFileId), null, force);
            return fileId;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 保存文件
     *
     * @param fileProtocol 文件相关数据对象
     * @return
     */
    public FileProtocol saveFile(FileProtocol fileProtocol) {
        String fileName = fileProtocol.getFileName();
        File file = fileProtocol.getFile();
        String fileId = buildFileId(fileName);
        checkIn(file, fileId);

        String fileType = fileName.substring(fileName.lastIndexOf('.'));
        String fileCode = fileType.toUpperCase(Locale.US);

        //文件为图片，生成多种规格
        if (getFileTypeCode(fileCode).equals(FileTypeCodeEnum.IMAGE.getCode())) {
            String imgSpecTmp = StringUtils.isBlank(fileProtocol.getImgSpec()) ? imgSpec : fileProtocol.getImgSpec();
            String[] specs = imgSpecTmp.split(",");
            if (specs.length > 0) {
                for (String spec : specs) {
                    genPicsReturnFileId(fileId, spec, false, false);
                }
            }
        }

        log.info("上传文件 fileId：" + fileId);

        fileProtocol.setFile(null);
        fileProtocol.setWebUrl(getWebUrl(fileId));
        fileProtocol.setFileId(fileId);
        return fileProtocol;
    }

    /**
     * 获取文件网络地址
     *
     * @param fileId 文件库 ID
     * @param spec   规格
     * @return
     */
    public String getWebUrl(String fileId, String spec) {
        if (StringUtils.isBlank(fileId)) {
            return null;
        }

        spec = StringUtils.isBlank(spec) ? "" : "_" + spec;
        return fileWebUrl + CommonUtil.insertFileNameSuffixToUrl(fileId, spec);
    }

    /**
     * 获取文件网络地址
     *
     * @param fileId 文件库 ID
     * @return
     */
    public String getWebUrl(String fileId) {
        return getWebUrl(fileId, "");
    }

    /**
     * 富文本图片解析（回显调用）
     *
     * @param text 富文本内容
     * @return
     */
    public String decodeHtmlImg(String text) {
        return StringUtils.isBlank(text) ? text : text.replaceAll(WEB_PLACEHOLDER, fileWebUrl);
    }

    /**
     * 富文本图片编码（保存调用）
     *
     * @param text 富文本内容
     * @return
     */
    public String encodeHtmlImg(String text) {
        return StringUtils.isBlank(text) ? text : text.replaceAll(fileWebUrl, WEB_PLACEHOLDER);
    }

    public FileProtocol createFile(String prefix, String suffix) throws IOException {
        File tempFile = File.createTempFile(prefix, suffix, new File(tmpPath));
        FileProtocol fileProtocol = new FileProtocol();
        fileProtocol.setFile(tempFile);
        fileProtocol.setFileName(tempFile.getName());
        return saveFile(fileProtocol);
    }


}
